package adams.data.utils;

import weka.core.Statistics;

/**
 * A helper class for SAX
 *
 * @author  dale (dale at waikato dot ac dot nz)
 */

public class SAXUtils {
  /**
   * Piecewise Aggregate Approximation.
   * @param inarray	input array
   * @param numwindows	number of pieces
   * @return		PAA
   */
  public static double[] PAA(double[] inarray, int numwindows){  
	// Width is the length of each the cut value  
    double width=(double)inarray.length/(double)numwindows;
    // Floor return the largest integer less than or equal to the specified number.
    int whole=(int)Math.floor(width);
    
    double[] ret = new double[numwindows];
    
    int currpos=0;
    double remainder1=0;
    double remainder2=width-(remainder1+whole);
    // each window will contain a symbol which describe is position between
    for (int i=0;i<numwindows;i++){
      // Sum permit to have the sum of the element of a selection of data
      double sum=remainder1*inarray[currpos];
      if (remainder1 != 0){
    	  currpos++;
      }
      for (int j=currpos;j<currpos+whole;j++){
    	  sum+=inarray[j];
      }
      currpos+=whole;
      if (i != numwindows-1){
    	  sum+=inarray[currpos]*remainder2;
      }
      remainder1=1.0-remainder2;
      whole=(int)Math.floor(width-remainder1);
      remainder2=width-(remainder1+whole);
      ret[i]=sum/width;
    }
    
    return(ret);
  }
  
  /**
   * Convert a row in original space into SAX labels. Assumes original space has been x normalised (rownorm?).
   * 
   * @param inarray
   * @param numwindows
   * @param bps
   * @return
   */
  public static double[] toSAX(double[] inarray, int numwindows, double[] bps){
	  // We obtain the average of the data in each window
    double[] sax=PAA(inarray,numwindows);
    
    for (int x=0;x<sax.length;x++){
      int saxlabel=bps.length;
      for (int i=0;i<bps.length;i++){	
    	  if (sax[x] < bps[i] ){
    		  saxlabel=i;
    		  break;
    	  }
      }   
      // We apply the label associate to the value of each PAA window
      sax[x]=saxlabel;
    }
    return(sax);
  }

  
  /**
   * Returns the maximum Z number for the given bin and breakpoints.
   * Assumes bins numbered from -infinity, starting from 0.
   * @param bin		bin number
   * @param bps		breakpoints
   * @return
   */
  private static double max(int bin, double[] bps){
    if (bin>=bps.length){
      return(Double.POSITIVE_INFINITY);
    }
    return(bps[bin]);
  }
  
  
  /**
   * Returns the minimum Z number for the given bin and breakpoints.
   * Assumes bins numbered from -infinity, starting from 0.
   * @param bin		bin number
   * @param bps		breakpoints
   * @return
   */
  private static double min(int bin, double[] bps){
    if (bin==0){
      return(Double.NEGATIVE_INFINITY);
    }
    return(bps[bin-1]);
  }
  
  /**
   * Calculate the distance matrix for use in the MINDIST function.
   * 
   * @param bps 	breakpoints
   * @return distance matrix
   */
  public static double[][] calcDistMatrix(double[] bps){
    double[][] ret=new double[bps.length+1][bps.length+1];
    for (int r=0;r<bps.length+1;r++){
      for (int c=0;c<bps.length+1;c++){
	if (Math.abs(r-c) <= 1){
	  ret[r][c]=0;
	} else {
	  ret[r][c]=bps[Math.max(r, c)-1] - bps[Math.min(r, c)];	
	}
      }
    }
    return(ret);
  }
  
  /**
   * Calculate the distance between 2 SAX vectors.
   * 
   * @param q		vector 1	
   * @param c		vector 2
   * @param distMatrix	distance matrix
   * @param orig_n	original vector length
   * @return		distance measure
   */
  public static double minDist(double[] q, double[] c, double[][] distMatrix, int orig_n){
    double ret=0;
    for (int i=0;i<c.length;i++){
      ret+=distMatrix[(int)q[i]][(int)c[i]] * distMatrix[(int)q[i]][(int)c[i]];
    }
    return(Math.sqrt(ret) * Math.sqrt((double)orig_n/(double)c.length));
  }
  
  /**
   * Calculate the break points for equal-frequency bins for a gaussian.
   * @param bins	number of bins
   * @return		break points. Z numbers.
   */
  public static double[] calcBreakPoints(int bins){
	  System.out.println ("Ola");
	    double[] ret = new double[bins-1];
	    double dbins=bins;
	    if (bins % 2==0){ //even
	      int num=(int)(dbins-2)/2;
	      double width=0.5/(dbins/2.0);
	      ret[(int)((dbins-2.0)/2.0)]=0;
	      for (int i=1;i<=num;i++){
	    	  ret[(int)((dbins-2.0)/2.0) + i]=Statistics.normalInverse(0.5+(i*width));
	    	  ret[(int)((dbins-2.0)/2.0) - i]=-ret[(int)((dbins-2.0)/2.0) + i];
	    	  System.out.println("++ " + ret[(int)((dbins-2.0)/2.0) + i] + "-- " + ret[(int)((dbins-2.0)/2.0) - i]);
	      }      
	    } 
	    else { //odd
	      int num=(int)(dbins-3)/2;
	      double width=1/dbins;
	      ret[(int)((dbins-1.0)/2.0)]=Statistics.normalInverse(0.5+(width/2.0));
	      ret[(int)((dbins-1.0)/2.0)-1]=-ret[(int)((dbins-1.0)/2.0)];
	      for (int i=1;i<=num;i++){
	    	  ret[(int)((dbins-1.0)/2.0) + (i)]=Statistics.normalInverse(0.5+(width/2.0)+(i*width));
	    	  ret[(int)((dbins-1.0)/2.0) - (i+1)]=-ret[(int)((dbins-1.0)/2.0) + i];
	    	  System.out.println("++ " +ret[(int)((dbins-1.0)/2.0) + (i)] + "-- " + ret[(int)((dbins-1.0)/2.0) - (i+1)]);
	      }      
	    }
	    return(ret);
	  }
  
  
  /**
   * Runs the algorithm from commandline.
   *
   * @param args	the options
   */
  public static void main(String[] args) {
    double[] get=calcBreakPoints(3);
    get=calcBreakPoints(4);
    //get=calcBreakPoints(7);
    //get=calcBreakPoints(9);
    double[][] get2=calcDistMatrix(get);
    get=calcBreakPoints(5);
  }
}
